﻿using RockBus.Configuration.Interfaces;
using RockBus.DataContracts;
using RockBus.Logging.Interfaces;
using RockBus.Messages.Interfaces;
using RockBus.PerformanceCounters.Interfaces;
using RockBus.Routing.Interfaces;
using System.Reflection;

namespace RockBus.MessageProcessors.SendPorts
{
    public class InMemoryPersistenceSendPort : IMessageProcessor
    {
        private const string OperationCounterName = "PersistMessage";

        public InMemoryPersistenceSendPort(
            ILogger logger,
            IPerformanceCounterFactory performanceCounterFactory,
            IReadOnlyConfiguration configuration,
            IMessageFactory messageFactory,
            IQueueManager queueManager
            )
        {
            this.Logger = logger;
            this.PerformanceCounterFactory = performanceCounterFactory;
            this.Configuration = configuration;
            this.MessageFactory = messageFactory;
            this.QueueManager = queueManager;
        }

        private ILogger Logger { get; set; }

        private IPerformanceCounterFactory PerformanceCounterFactory { get; set; }

        private IReadOnlyConfiguration Configuration { get; set; }

        private IMessageFactory MessageFactory { get; set; }

        private IQueueManager QueueManager { get; set; }

        public MessageProcessorEnum ProcessingStep { get { return MessageProcessorEnum.PersistenceSendPort; } }

        public void ProcessMessage(IMessageContext messageContext)
        {
            this.Logger.LogDebug(MethodBase.GetCurrentMethod(), "{0}", messageContext);

            var oc = this.PerformanceCounterFactory.CreateOperationCounter(OperationCounterName);
            oc.Start();

            try
            {
                if (messageContext.ProcessingData.MessageDirection == MessageDirectionEnum.End)
                {
                    // No action needed
                    return;
                }
                else if (messageContext.Exception != null)
                {
                    // Note: we do not abort the message, because we want to go into in-memory retries
                    this.QueueManager.ProcessRetry(messageContext);
                    return;
                }

                // Set ProcessingData for messages to publish back
                //  We mimic that the message was persisted in the mean time
                messageContext.ProcessingData.MessageDirection++;

                foreach (var rh in messageContext.RoutingHeaders)
                {
                    // Override the delivery attempt parameters if they weren't set
                    if (!rh.SubscriptionDetail.DeliveryAttemptIntervalSecs.HasValue)
                    {
                        rh.SubscriptionDetail.DeliveryAttemptIntervalSecs = this.Configuration.InternalConfiguration.DefaultTransientDeliveryAttemptIntervalSecs;
                    }
                    if (!rh.SubscriptionDetail.MaxDeliveryAttemptCount.HasValue)
                    {
                        rh.SubscriptionDetail.MaxDeliveryAttemptCount = this.Configuration.InternalConfiguration.DefaultTransientMaxDeliveryAttemptCount;
                    }

                    // Each RoutingHeader gets it's own copy of the ProcessingData
                    rh.ProcessingData = new ProcessingData(messageContext.ProcessingData);
                    var messageContextToPublish = this.MessageFactory.CreateMessageContext(messageContext.Message.Message, rh);
                    this.QueueManager.ProcessMessage(messageContextToPublish);
                }

                // Finish up the incoming request or response
                messageContext.ProcessingData.MessageDirection = MessageDirectionEnum.End;
                this.QueueManager.ProcessMessage(messageContext);
            }
            catch
            {
                oc.Abort();
                throw;
            }
            finally
            {
                oc.Complete();
            }
        }
    }
}